/************************************************************************/
// Copyright (C) 2016, han_gangbiao. All rights reserved.
// Use of this source code is governed by a GPL-v2 license that can be found in the License file.
// 
// [Time]:      2016-1-4 21:53:17
// [Author]:    han_gangbiao [http://my.oschina.net/jackhen]
// [Info]:      
/************************************************************************/

#include "xtime.h"

BEG_NS_SMART

//////////////////////////////////////////////////////////////////////////
#define refresh(timestamp)                              \
struct tm *timeptr = localtime(&(timestamp).time);      \
    year = timeptr->tm_year;                            \
    month = timeptr->tm_mon;                            \
    day = timeptr->tm_mday;                             \
    hour = timeptr->tm_hour;                            \
    minute = timeptr->tm_min;                           \
    second = timeptr->tm_sec;                           \
    millisecond = (timestamp).millitm;                  \
    wday = timeptr->tm_wday;                            \
    yday = timeptr->tm_yday;                            \
    isdst = timeptr->tm_isdst;
//////////////////////////////////////////////////////////////////////////
mytime_t::mytime_t()
{
    ftime(&m_timeStamp);

    struct tm* tmp = localtime(&m_timeStamp.time);
    year = tmp->tm_year;
    month = tmp->tm_mon;
    day = tmp->tm_mday;
    hour = tmp->tm_hour;
    minute = tmp->tm_min;
    second = tmp->tm_sec;
    millisecond = m_timeStamp.millitm;

    wday = tmp->tm_wday;
    yday = tmp->tm_yday;
    isdst = tmp->tm_isdst;
}

mytime_t::mytime_t( mytime_t& tm )
{
    this->m_timeStamp = tm.m_timeStamp;

    refresh(this->m_timeStamp);
}

mytime_t::mytime_t( uint64 timestamp, unsigned short msec /*= 0*/ )
{
    time_t the_timestamp = (time_t)timestamp;
    m_timeStamp.time = the_timestamp;
    m_timeStamp.millitm = msec;

    struct tm* tmp = localtime(&m_timeStamp.time);
    year = tmp->tm_year;
    month = tmp->tm_mon;
    day = tmp->tm_mday;
    hour = tmp->tm_hour;
    minute = tmp->tm_min;
    second = tmp->tm_sec;
    millisecond = m_timeStamp.millitm;

    wday = tmp->tm_wday;
    yday = tmp->tm_yday;
    isdst = tmp->tm_isdst;
}

mytime_t::mytime_t( int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, unsigned short msec /*= 0*/ )
{
    struct tm *timeptr = new struct tm;
    memset(timeptr, 0, sizeof(struct tm));

    timeptr->tm_year = nYear;
    timeptr->tm_mon = nMonth;
    timeptr->tm_mday = nDay;
    timeptr->tm_hour = nHour;
    timeptr->tm_min = nMin;
    timeptr->tm_sec = nSec;

    time_t the_timestamp = mktime(timeptr);

    m_timeStamp.time = the_timestamp;
    m_timeStamp.millitm = msec;

    delete timeptr;
}

uint64 mytime_t::getTimestamp() const
{
    return (uint64)m_timeStamp.time;
}

std::string mytime_t::format( FormatMask fmt /*= fmtGeneral*/ )
{
    std::string str;
    switch(fmt)
    {
    case fmtGeneral:	//YYYY-mm-dd HH:MM:SS
    case fmtHour24:
        {
            char buf[256];
            struct tm *tm_ptr = localtime(&m_timeStamp.time);
            strftime(buf, sizeof(buf),"%Y-%m-%d %H:%M:%S", tm_ptr);
            str = buf;
        }
        break;
    case fmtHour12:		//YYYY-mm-dd II:MM:SS AM./PM.
        {
            char buf[256];
            struct tm *tm_ptr = localtime(&m_timeStamp.time);
            strftime(buf, sizeof(buf),"%Y-%m-%d %I:%M:%S %p", tm_ptr);
            str = buf;
        }
        break;
    case fmtTimeStr:
        {
            char buf[256];
            struct tm *tm_ptr = localtime(&m_timeStamp.time);
            strftime(buf, sizeof(buf),"%Y%m%d%H%M%S", tm_ptr);
            str = buf;
        }
        break;
    case fmtTimeStrMS:
        {
            char buf[256];
            struct tm *tm_ptr = localtime(&m_timeStamp.time);
            strftime(buf, sizeof(buf),"%Y%m%d%H%M%S", tm_ptr);
            str = buf;
            sprintf(buf, "%03d", millisecond);
            str += buf;
        }
        break;
    default:
        {
            char buf[256];
            struct tm *tm_ptr = localtime(&m_timeStamp.time);
            strftime(buf, sizeof(buf),"%Y-%m-%d %H:%M:%S", tm_ptr);
            str = buf;
        }
        break;
    }

    return str;
}

std::string mytime_t::format( const char* fmt )
{
    std::string str = "";
    if(fmt == NULL)
    {
        return str.c_str();
    }

    char buf[256];
    struct tm *tm_ptr = localtime(&m_timeStamp.time);
    strftime(buf, sizeof(buf),fmt, tm_ptr);
    str = buf;

    return str;
}

//////////////////////////////////////////////////////////////////////////
/*static*/
uint64 mytime_t::getCurrentTimestamp()
{
    timeb tmp;
    ftime(&tmp);
    return (uint64)tmp.time;
}

uint64 mytime_t::getCurrentTimestamp_ms()
{
#ifdef OS_WIN
	ULARGE_INTEGER fti;
	GetSystemTimeAsFileTime( (LPFILETIME)&fti ); // is 100ns == 0.0001ms
	// remove seconds between 1601-01-01 to 1970-01-01
	return fti.QuadPart / (10 * 1000) - ( 11644473600LL * 1000LL );
#else
    //Linux
    struct timeval tv = {0};
    gettimeofday(&tv, 0);
    uint64 retTime=tv.tv_sec*1000LL;
    retTime+=tv.tv_usec/1000;
    return retTime;
#endif
}

uint64 mytime_t::getCurrentTimestamp_us()
{
#ifdef OS_WIN
	ULARGE_INTEGER fti;
	GetSystemTimeAsFileTime( (LPFILETIME)&fti ); // is 100ns == 0.0001ms
	// remove seconds between 1601-01-01 to 1970-01-01
	return fti.QuadPart / 10 - ( 11644473600LL * 1000LL * 1000LL );
#else
    //Linux
    struct timeval tv = {0};
    gettimeofday(&tv, 0);
    uint64 retTime=tv.tv_sec*1000LL*1000LL;
    retTime+=tv.tv_usec;
    return retTime;
#endif
}

uint64 mytime_t::getCurrentClock()
{
#ifdef OS_WIN
    return (uint64)timeGetTime(); //ms. The system time is the time elapsed since the system started
#else
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return (uint64)((int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
#endif
}

//////////////////////////////////////////////////////////////////////////
mytime_t mytime_t::operator+(int64 msec)
{
	mytime_t tmp(*this);

    if(msec > 0)
    {
        int64 sec = msec/1000;	//s
        int64 ms = msec%1000;	//ms
        tmp.m_timeStamp.time += (time_t)sec;
        tmp.m_timeStamp.millitm += (unsigned short)ms;

        if(tmp.m_timeStamp.millitm >= 1000)
        {
            tmp.m_timeStamp.time += 1;
            tmp.m_timeStamp.millitm -= 1000;
        }
        refresh(tmp.m_timeStamp);
        return tmp;
    }
    else if(msec < 0)
    {
        int64 sec = ((-1)*msec)/1000;
        int64 ms = ((-1)*msec)%1000;

        tmp.m_timeStamp.time -= sec;

        if(tmp.m_timeStamp.millitm >= ms)
        {
            tmp.m_timeStamp.millitm -= (unsigned short)ms;
        }
        else
        {
            tmp.m_timeStamp.time -= 1;
            tmp.m_timeStamp.millitm = tmp.m_timeStamp.millitm + 1000 - (unsigned short)ms;
        }

        refresh(tmp.m_timeStamp);
        return tmp;
    }
    else
    {
        return tmp;
    }
}

mytime_t mytime_t::operator-(int64 msec)
{
	mytime_t tmp(*this);

    if(msec < 0)
    {
        int64 sec = ((-1)*msec)/1000;	//s
        int64 ms = ((-1)*msec)%1000;	//ms
        tmp.m_timeStamp.time += (time_t)sec;
        tmp.m_timeStamp.millitm += (unsigned short)ms;

        if(tmp.m_timeStamp.millitm >= 1000)
        {
            tmp.m_timeStamp.time += 1;
            tmp.m_timeStamp.millitm -= 1000;
        }
        refresh(tmp.m_timeStamp);
        return tmp;
    }
    else if(msec > 0)
    {
        int64 sec = msec/1000;
        int64 ms = msec%1000;

        tmp.m_timeStamp.time -= (time_t)sec;

        if(tmp.m_timeStamp.millitm >= ms)
        {
            tmp.m_timeStamp.millitm -= (unsigned short)ms;
        }
        else
        {
            tmp.m_timeStamp.time -= 1;
            tmp.m_timeStamp.millitm = tmp.m_timeStamp.millitm + 1000 - (unsigned short)ms;
        }

        refresh(tmp.m_timeStamp);
        return tmp;
    }
    else
    {
        return tmp;
    }
}

mytime_t& mytime_t::operator+=(int64 msec)
{
    if(msec > 0)
    {
        int64 sec = msec/1000;	//s
        int64 ms = msec%1000;	//ms
        this->m_timeStamp.time += (time_t)sec;
        this->m_timeStamp.millitm += (unsigned short)ms;

        if(this->m_timeStamp.millitm >= 1000)
        {
            this->m_timeStamp.time += 1;
            this->m_timeStamp.millitm -= 1000;
        }
        refresh(this->m_timeStamp);
        return *this;
    }
    else if(msec < 0)
    {
        int64 sec = ((-1)*msec)/1000;
        int64 ms = ((-1)*msec)%1000;

        this->m_timeStamp.time -= (time_t)sec;

        if(this->m_timeStamp.millitm >= ms)
        {
            this->m_timeStamp.millitm -= (unsigned short)ms;
        }
        else
        {
            this->m_timeStamp.time -= 1;
            this->m_timeStamp.millitm = this->m_timeStamp.millitm + 1000 - (unsigned short)ms;
        }

        refresh(this->m_timeStamp);
        return *this;
    }
    else
    {
        return *this;
    }
}

mytime_t& mytime_t::operator-=(int64 msec)
{
    if(msec < 0)
    {
        int64 sec = ((-1)*msec)/1000;	//s
        int64 ms = ((-1)*msec)%1000;	//ms
        this->m_timeStamp.time += (time_t)sec;
        this->m_timeStamp.millitm += (unsigned short)ms;

        if(this->m_timeStamp.millitm >= 1000)
        {
            this->m_timeStamp.time += 1;
            this->m_timeStamp.millitm -= 1000;
        }
        refresh(this->m_timeStamp);
        return *this;
    }
    else if(msec > 0)
    {
        int64 sec = msec/1000;
        int64 ms = msec%1000;

        this->m_timeStamp.time -= (time_t)sec;

        if(this->m_timeStamp.millitm >= ms)
        {
            this->m_timeStamp.millitm -= (unsigned short)ms;
        }
        else
        {
            this->m_timeStamp.time -= 1;
            this->m_timeStamp.millitm = this->m_timeStamp.millitm + 1000 - (unsigned short)ms;
        }

        refresh(this->m_timeStamp);
        return *this;
    }
    else
    {
        return *this;
    }
}

int64 mytime_t::operator-(const mytime_t& tm)
{
    return (this->m_timeStamp.time*1000+this->m_timeStamp.millitm) - (tm.m_timeStamp.time*1000+tm.m_timeStamp.millitm);
}

bool mytime_t::operator==(const mytime_t& tm)
{
    if((this->m_timeStamp.time == tm.m_timeStamp.time)
        &&(this->m_timeStamp.millitm == tm.m_timeStamp.millitm))
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool mytime_t::operator!=(const mytime_t& tm)
{
    if((this->m_timeStamp.time != tm.m_timeStamp.time)
        ||(this->m_timeStamp.millitm != tm.m_timeStamp.millitm))
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool mytime_t::operator<(const mytime_t& tm)
{
    return (this->m_timeStamp.time*1000+this->m_timeStamp.millitm) < (tm.m_timeStamp.time*1000+tm.m_timeStamp.millitm);
}

bool mytime_t::operator>(const mytime_t& tm)
{
    return (this->m_timeStamp.time*1000+this->m_timeStamp.millitm) > (tm.m_timeStamp.time*1000+tm.m_timeStamp.millitm);
}

END_NS_SMART
